//
// Created by twetec on 25-10-18.
//

#include <gtest/gtest.h>
#include <asio2/tcp/tcp_client.hpp>

#include <network_prober/probe_detect/handle_tcp_probe_detect.h>

namespace tests::probe_detect
{
namespace
{
TEST(HandleTCPProbeDetectIPv4Test, testAsio2TCP)
{
    std::string_view host = "192.168.0.108";
    //std::string_view host = "fe80::dc05:d962:f568:39b7"; // ipv6 windows
    //std::string_view host = "fe80::bb00:5a10:d713:d293%eth0"; // ipv6 linux
    std::string_view port = "9090";

    asio2::tcp_client client;

    // disable auto reconnect, default reconnect option is "enable"
    //client.set_auto_reconnect(false);

    // enable auto reconnect and use custom delay, default delay is 1 seconds
    client.set_auto_reconnect(true, std::chrono::milliseconds(2000));

    client.bind_connect([&]()
    {
        std::cout << "connected" << std::endl;

        if (asio2::get_last_error())
            printf("connect failure : %d %s\n",
                asio2::last_error_val(), asio2::last_error_msg().c_str());
        else
            printf("connect success : %s %u\n",
                client.local_address().c_str(), client.local_port());

        // has no error, it means connect success, we can send data at here
        if (!asio2::get_last_error())
        {
            client.async_send("<abcdefghijklmnopqrstovuxyz0123456789>");
        }
    }).bind_disconnect([&]()
    {
        printf("disconnect : %d %s\n",
            asio2::last_error_val(), asio2::last_error_msg().c_str());
    }).bind_recv([&](std::string_view data)
    {
        std::cout << "recv: " << data << std::endl;

        printf("recv : %zu %.*s\n", data.size(), (int)data.size(), data.data());

        // client.async_send(data);
    });

    client.start(host, port);

    while (std::getchar() != '\n');
}

TEST(HandleTCPProbeDetectIPv4Test, testPosix)
{
    // 创建UDP套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Socket creation failed" << std::endl;
        FAIL();
    }

    // 设置接收超时
    struct timeval tv;
    tv.tv_sec = 5000 / 1000;
    tv.tv_usec = (5000 % 1000) * 1000;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    // const char *device_name = "fd00:7860:5bfc:44d0:9f00:cea8:5398:bcee";
    // std::cout << sizeof(device_name) << std::endl;
    // std::cout << strlen(device_name) << std::endl;
    // if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, device_name, strlen(device_name)+1) == -1) {
    //     std::cerr << "Failed to bind to device: " << strerror(errno) << std::endl;
    // }

    // 绑定本机地址
    struct sockaddr_in clientAddr;
    memset(&clientAddr, 0, sizeof(clientAddr));
    clientAddr.sin_family = AF_INET;
    clientAddr.sin_port = htons(0);
    if (inet_pton(AF_INET, "192.168.0.108", &clientAddr.sin_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 将监听的本地IP和端口与文件描述符绑定
    if (bind(sockfd, (sockaddr *)&clientAddr, sizeof(clientAddr)) < 0) {
        std::cerr << "Invalid interface: " << strerror(errno) << std::endl;
        close(sockfd);
        FAIL();
    }

    // 准备服务器地址
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(9090);
    if (inet_pton(AF_INET, "192.168.0.104", &servaddr.sin_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        std::cerr << "连接失败.." << std::endl;
        close(sockfd);
        FAIL();
    }

    close(sockfd);
}

TEST(HandleTCPProbeDetectIPv4Test, testDetect)
{
    // 创建探测指令对象
    auto probeDetectInstructionPtr = std::make_shared<network_prober::probe_detect::HandleTCPProbeDetectIPv4>("192.168.0.108", "192.168.0.104", 9090);
    // 监听指令执行结果(延迟了对象生命周期)
    probeDetectInstructionPtr->setOnDetectedCallback([](network_prober::probe_detect::TCPProbeDetectResultIPv4&& result) {
        std::cout << result << std::endl;
    });
    // 执行指令
    probeDetectInstructionPtr->detect();

    while (std::getchar() != '\n');
}

TEST(HandleTCPProbeDetectIPv6Test, testAsio2TCP)
{
    // std::string_view host = "192.168.0.104";
    // std::string_view host = "fd00:7860:5bfc:44d0::1000"; // ipv6 windows
    // std::string_view host = "fe80::bb00:5a10:d713:d293%eth0"; // ipv6 linux
    std::string_view host = "fe80::e974:78cb:ae7:219d%wlxe0e1a9384141";
    std::string_view port = "9090";

    asio2::tcp_client client;

    // disable auto reconnect, default reconnect option is "enable"
    //client.set_auto_reconnect(false);

    // enable auto reconnect and use custom delay, default delay is 1 seconds
    client.set_auto_reconnect(true, std::chrono::milliseconds(2000));

    client.bind_connect([&]()
    {
        std::cout << "connected" << std::endl;

        if (asio2::get_last_error())
            printf("connect failure : %d %s\n",
                asio2::last_error_val(), asio2::last_error_msg().c_str());
        else
            printf("connect success : %s %u\n",
                client.local_address().c_str(), client.local_port());

        // has no error, it means connect success, we can send data at here
        if (!asio2::get_last_error())
        {
            client.async_send("<abcdefghijklmnopqrstovuxyz0123456789>");
        }
    }).bind_disconnect([&]()
    {
        printf("disconnect : %d %s\n",
            asio2::last_error_val(), asio2::last_error_msg().c_str());
    }).bind_recv([&](std::string_view data)
    {
        std::cout << "recv: " << data << std::endl;

        printf("recv : %zu %.*s\n", data.size(), (int)data.size(), data.data());

        // client.async_send(data);
    });

    client.start(host, port);

    while (std::getchar() != '\n');
}

TEST(HandleTCPProbeDetectIPv6Test, testPosix)
{
    // 创建UDP套接字
    int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Socket creation failed" << std::endl;
        FAIL();
    }

    // 设置接收超时
    struct timeval tv;
    tv.tv_sec = 5000 / 1000;
    tv.tv_usec = (5000 % 1000) * 1000;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    // const char *device_name = "fd00:7860:5bfc:44d0:9f00:cea8:5398:bcee";
    // std::cout << sizeof(device_name) << std::endl;
    // std::cout << strlen(device_name) << std::endl;
    // if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, device_name, strlen(device_name)+1) == -1) {
    //     std::cerr << "Failed to bind to device: " << strerror(errno) << std::endl;
    // }

    // // 绑定本机地址
    // struct sockaddr_in6 clientAddr;
    // memset(&clientAddr, 0, sizeof(clientAddr));
    // clientAddr.sin6_family = AF_INET6;
    // clientAddr.sin6_port = htons(0);
    // if (inet_pton(AF_INET6, "fe80::e974:78cb:ae7:219d", &clientAddr.sin6_addr) <= 0) {
    //     std::cerr << "Invalid address" << std::endl;
    //     close(sockfd);
    //     FAIL();
    // }

    // // 将监听的本地IP和端口与文件描述符绑定
    // if (bind(sockfd, (sockaddr *)&clientAddr, sizeof(clientAddr)) < 0) {
    //     std::cerr << "Invalid interface: " << strerror(errno) << std::endl;
    //     close(sockfd);
    //     FAIL();
    // }

    // 准备服务器地址
    struct sockaddr_in6 servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin6_family = AF_INET6;
    servaddr.sin6_port = htons(9090);
    if (inet_pton(AF_INET6, "fd00:7860:5bfc:44d0::1000", &servaddr.sin6_addr) <= 0) {
        std::cerr << "Invalid address" << std::endl;
        close(sockfd);
        FAIL();
    }

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        std::cerr << "连接失败.." << std::endl;
        close(sockfd);
        FAIL();
    }

    close(sockfd);
}

TEST(HandleTCPProbeDetectIPv6Test, testDetect)
{
    // 创建探测指令对象
    auto probeDetectInstructionPtr = std::make_shared<network_prober::probe_detect::HandleTCPProbeDetectIPv6>("fe80::e974:78cb:ae7:219d", "fd00:7860:5bfc:44d0::1001", 9090);
    // 监听指令执行结果(延迟了对象生命周期)
    probeDetectInstructionPtr->setOnDetectedCallback([](network_prober::probe_detect::TCPProbeDetectResultIPv6&& result) {
        std::cout << result << std::endl;
    });
    // 执行指令
    probeDetectInstructionPtr->detect();

    while (std::getchar() != '\n');
}

}
}
